package org.ecbox.android.sdk;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.ecbox.android.sdk.internal.parser.json.ObjectJsonParser;
import org.ecbox.android.sdk.internal.util.RequestParametersHolder;
import org.ecbox.android.sdk.internal.util.StringUtils;
import org.ecbox.android.sdk.internal.util.WebUtils;
import org.ecbox.android.sdk.internal.util.codec.Base64;
import org.ecbox.android.sdk.internal.util.json.JSONReader;
import org.ecbox.android.sdk.internal.util.json.JSONValidatingReader;

/**
 * 系统工具类
 * 
 * File: EopUtils.java<br/>
 * Description: <br/>
 * 
 * Copyright: Copyright (c) 2012 ecbox.com<br/>
 * Company: ECBOX,Inc.<br/>
 * 
 * @author chenxiaochun
 * @date 2013-2-3
 * @version 1.0
 */
public abstract class EopUtils {
	private static String localIp;

	private EopUtils() {
	}

	/**
	 * 给TOP请求签名。
	 * 
	 * @param requestHolder
	 *            所有字符型的TOP请求参数
	 * @param secret
	 *            签名密钥
	 * @return 签名
	 * @throws IOException
	 */
	public static String signEopRequest(RequestParametersHolder requestHolder, String secret) throws IOException {
		// 第一步：把字典按Key的字母顺序排序
		Map<String, String> sortedParams = new TreeMap<String, String>();
		EopHashMap appParams = requestHolder.getApplicationParams();
		if (appParams != null && appParams.size() > 0) {
			sortedParams.putAll(appParams);
		}
		EopHashMap protocalMustParams = requestHolder.getProtocalMustParams();
		if (protocalMustParams != null && protocalMustParams.size() > 0) {
			sortedParams.putAll(protocalMustParams);
		}
		EopHashMap protocalOptParams = requestHolder.getProtocalOptParams();
		if (protocalOptParams != null && protocalOptParams.size() > 0) {
			sortedParams.putAll(protocalOptParams);
		}

		Set<Entry<String, String>> paramSet = sortedParams.entrySet();

		// 第二步：把所有参数名和参数值串在一起
		StringBuilder query = new StringBuilder(secret);
		for (Entry<String, String> param : paramSet) {
			// 允许空字符串
			if (null != param.getValue()) {
				query.append(param.getKey()).append(param.getValue());
			}
		}

		// 第三步：使用MD5加密
		byte[] bytes = encryptMD5(query.toString());

		// 第四步：把二进制转化为大写的十六进制
		return byte2hex(bytes);
	}

	/**
	 * 给TOP请求签名。
	 * 
	 * @param requestHolder
	 *            所有字符型的TOP请求参数
	 * @param secret
	 *            签名密钥
	 * @param isHmac
	 *            是否为HMAC方式加密
	 * @return 签名
	 * @throws IOException
	 */
	public static String signEopRequestNew(RequestParametersHolder requestHolder, String secret, boolean isHmac)
			throws IOException {
		// 第一步：把字典按Key的字母顺序排序
		Map<String, String> sortedParams = new TreeMap<String, String>();
		EopHashMap appParams = requestHolder.getApplicationParams();
		if (appParams != null && appParams.size() > 0) {
			sortedParams.putAll(appParams);
		}
		EopHashMap protocalMustParams = requestHolder.getProtocalMustParams();
		if (protocalMustParams != null && protocalMustParams.size() > 0) {
			sortedParams.putAll(protocalMustParams);
		}
		EopHashMap protocalOptParams = requestHolder.getProtocalOptParams();
		if (protocalOptParams != null && protocalOptParams.size() > 0) {
			sortedParams.putAll(protocalOptParams);
		}

		Set<Entry<String, String>> paramSet = sortedParams.entrySet();

		// 第二步：把所有参数名和参数值串在一起
		StringBuilder query = new StringBuilder();
		if (!isHmac) {
			query.append(secret);
		}
		for (Entry<String, String> param : paramSet) {
			if (param.getValue() != null) {
				query.append(param.getKey()).append(param.getValue());
			}
		}

		// 第三步：使用MD5/HMAC加密
		byte[] bytes;
		if (isHmac) {
			bytes = encryptHMAC(query.toString(), secret);
		} else {
			query.append(secret);
			bytes = encryptMD5(query.toString());
		}

		// 第四步：把二进制转化为大写的十六进制
		return byte2hex(bytes);
	}

	private static byte[] encryptHMAC(String data, String secret) throws IOException {
		byte[] bytes = null;
		try {
			SecretKey secretKey = new SecretKeySpec(secret.getBytes(Constants.CHARSET_UTF8), "HmacMD5");
			Mac mac = Mac.getInstance(secretKey.getAlgorithm());
			mac.init(secretKey);
			bytes = mac.doFinal(data.getBytes(Constants.CHARSET_UTF8));
		} catch (GeneralSecurityException gse) {
			String msg = getStringFromException(gse);
			throw new IOException(msg);
		}
		return bytes;
	}

	private static String getStringFromException(Throwable e) {
		String result = "";
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		PrintStream ps = new PrintStream(bos);
		e.printStackTrace(ps);
		try {
			result = bos.toString(Constants.CHARSET_UTF8);
		} catch (IOException ioe) {
		}
		return result;
	}

	private static byte[] encryptMD5(String data) throws IOException {
		byte[] bytes = null;
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			bytes = md.digest(data.getBytes(Constants.CHARSET_UTF8));
		} catch (GeneralSecurityException gse) {
			String msg = getStringFromException(gse);
			throw new IOException(msg);
		}
		return bytes;
	}

	private static String byte2hex(byte[] bytes) {
		StringBuilder sign = new StringBuilder();
		for (int i = 0; i < bytes.length; i++) {
			String hex = Integer.toHexString(bytes[i] & 0xFF);
			if (hex.length() == 1) {
				sign.append("0");
			}
			sign.append(hex.toUpperCase());
		}
		return sign.toString();
	}

	/**
	 * 生成 md5
	 * 
	 * @param str
	 * @return
	 */
	public static String md5(String str) {
		byte[] bytes;
		try {
			bytes = encryptMD5(str);
			StringBuilder sign = new StringBuilder();
			for (int i = 0; i < bytes.length; i++) {
				String hex = Integer.toHexString(bytes[i] & 0xFF);
				if (hex.length() == 1) {
					sign.append("0");
				}
				sign.append(hex);
			}
			return sign.toString();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 验证TOP回调地址的签名是否合法。要求所有参数均为已URL反编码的。
	 * 
	 * @param topParams
	 *            TOP私有参数（未经BASE64解密）
	 * @param topSession
	 *            TOP私有会话码
	 * @param topSign
	 *            TOP回调签名
	 * @param appKey
	 *            应用公钥
	 * @param appSecret
	 *            应用密钥
	 * @return 验证成功返回true，否则返回false
	 * @throws IOException
	 */
	public static boolean verifyEopResponse(String topParams, String topSession, String topSign, String appKey,
			String appSecret) throws IOException {
		StringBuilder result = new StringBuilder();
		result.append(appKey).append(topParams).append(topSession).append(appSecret);
		byte[] bytes = encryptMD5(result.toString());
		Base64 encoder = new Base64();
		return new String(encoder.encode(bytes)).equals(topSign);
	}

	/**
	 * 解释TOP回调参数为键值对。(用于container回调时参数的解析)
	 * 
	 * @param topParams
	 *            经过BASE64编码的字符串
	 * @return 键值对
	 * @throws IOException
	 */
	public static Map<String, String> decodeEopParams(String topParams) throws IOException {
		return decodeEopParams(topParams, Constants.CHARSET_GBK);
	}

	/**
	 * 解释TOP回调参数为键值对。(用于container回调时参数的解析)
	 * 
	 * @param topParams
	 *            经过BASE64编码的字符串
	 * @param charset
	 *            字符集编码
	 * @return
	 * @throws IOException
	 */
	public static Map<String, String> decodeEopParams(String topParams, String charset) throws IOException {
		if (StringUtils.isEmpty(topParams)) {
			return null;
		}

		Base64 decoder = new Base64();
		byte[] buffer = decoder.decode(WebUtils.decode(topParams).getBytes());
		String originTopParams = new String(buffer, charset);

		return WebUtils.splitUrlQuery(originTopParams);
	}

	/**
	 * 获取文件的真实后缀名。目前只支持JPG, GIF, PNG, BMP四种图片文件。
	 * 
	 * @param bytes
	 *            文件字节流
	 * @return JPG, GIF, PNG or null
	 */
	public static String getFileSuffix(byte[] bytes) {
		if (bytes == null || bytes.length < 10) {
			return null;
		}

		if (bytes[0] == 'G' && bytes[1] == 'I' && bytes[2] == 'F') {
			return "GIF";
		} else if (bytes[1] == 'P' && bytes[2] == 'N' && bytes[3] == 'G') {
			return "PNG";
		} else if (bytes[6] == 'J' && bytes[7] == 'F' && bytes[8] == 'I' && bytes[9] == 'F') {
			return "JPG";
		} else if (bytes[0] == 'B' && bytes[1] == 'M') {
			return "BMP";
		} else {
			return null;
		}
	}

	/**
	 * 获取文件的真实媒体类型。目前只支持JPG, GIF, PNG, BMP四种图片文件。
	 * 
	 * @param bytes
	 *            文件字节流
	 * @return 媒体类型(MEME-TYPE)
	 */
	public static String getMimeType(byte[] bytes) {
		String suffix = getFileSuffix(bytes);
		String mimeType;

		if ("JPG".equals(suffix)) {
			mimeType = "image/jpeg";
		} else if ("GIF".equals(suffix)) {
			mimeType = "image/gif";
		} else if ("PNG".equals(suffix)) {
			mimeType = "image/png";
		} else if ("BMP".equals(suffix)) {
			mimeType = "image/bmp";
		} else {
			mimeType = "application/octet-stream";
		}

		return mimeType;
	}

	/**
	 * 清除字典中值为空的项。
	 * 
	 * @param <V>
	 *            泛型
	 * @param map
	 *            待清除的字典
	 * @return 清除后的字典
	 */
	public static <V> Map<String, V> cleanupMap(Map<String, V> map) {
		if (map == null || map.isEmpty()) {
			return null;
		}

		Map<String, V> result = new HashMap<String, V>(map.size());
		Set<Entry<String, V>> entries = map.entrySet();

		for (Entry<String, V> entry : entries) {
			if (entry.getValue() != null) {
				result.put(entry.getKey(), entry.getValue());
			}
		}

		return result;
	}

	/**
	 * 把JSON字符串转化为Map结构。
	 * 
	 * @param body
	 *            JSON字符串
	 * @return Map结构
	 */
	public static Map<?, ?> parseJson(String body) {
		JSONReader jr = new JSONValidatingReader();
		Object obj = jr.read(body);
		if (obj instanceof Map<?, ?>) {
			return (Map<?, ?>) obj;
		} else {
			return null;
		}
	}

	/**
	 * 把JSON字符串解释为对象结构。
	 * 
	 * @param <T>
	 *            API响应类型
	 * @param json
	 *            JSON字符串
	 * @param clazz
	 *            API响应类
	 * @return API响应对象
	 */
	public static <T extends EopResponse> T parseResponse(String json, Class<T> clazz) throws ApiException {
		ObjectJsonParser<T> parser = new ObjectJsonParser<T>(clazz);
		return parser.parse(json);
	}

	/**
	 * 获取本机的网络IP
	 */
	public static String getLocalNetWorkIp() {
		if (localIp != null) {
			return localIp;
		}
		try {
			Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces();
			InetAddress ip = null;
			while (netInterfaces.hasMoreElements()) {// 遍历所有的网卡
				NetworkInterface ni = (NetworkInterface) netInterfaces.nextElement();
				Enumeration<InetAddress> addresss = ni.getInetAddresses();
				while (addresss.hasMoreElements()) {
					InetAddress address = addresss.nextElement();
					if (address instanceof Inet4Address) {// 这里暂时只获取ipv4地址
						ip = address;
						break;
					}
				}
				if (ip != null) {
					break;
				}
			}
			if (ip != null) {
				localIp = ip.getHostAddress();
			} else {
				localIp = "127.0.0.1";
			}
		} catch (Exception e) {
			localIp = "127.0.0.1";
		}
		return localIp;
	}

}
